#ifndef __AUDIOHOSTFORCA_H__
#define __AUDIOHOSTFORCA_H__
#if defined(WIN32)&defined(WINVER>0x600)
#include <MMDeviceAPI.h>
#include <AudioClient.h>
#include <AudioPolicy.h>
#include <functiondiscoverykeys.h>
#include <EndpointVolume.h>
#include <windows.h>
#include "AudioHost.h"
#include "DsoundOutput.h"
#include "wavvsthost.h"

template <class T> void SafeRelease(T **ppT)
{
    if (*ppT)
    {
        (*ppT)->Release();
        *ppT = NULL;
    }
}

/** The CAudioHostForCA class.
 *  CAudioHost class for Core Audio APIs.
 *	Interface compatible for windows vista and later versions of windows.
 */
class CAudioHostForCA :public CAudioHost, IAudioSessionEvents, IMMNotificationClient
{
protected:
	enum OUTPUT_DRIVER_TYPE{
		DIRECT_SOUND,
		KERNEL_STREAM,
		CORE_AUDIO
	};
private:
	static BOOL m_fModelsInited;
	static void InitModels();
protected:
	CDsoundOutput*		m_pDSO;
	OUTPUT_DRIVER_TYPE	m_eDrvType;
	BOOL				m_fIsVista;
	int					m_nLDevs;
	char				m_pszLDevNames[16][128];
	int					m_nCDevs;
	char				m_pszCDevNames[16][128];
	BOOL				m_fIsRun;
	int					m_iCurLDev;
	int					m_iCurCDev;
	int					m_iDefLDev;
	int					m_iDefCDev;
	CWavVstHost**		m_pVSTArray;
	HANDLE				m_hThread;
	int					m_nBufferSize;
	CRITICAL_SECTION	m_cs;//Used for Vsts
	int					m_iInChannels;
	int					m_nVolume;
	WAVEFORMATEXTENSIBLE		m_wfx;
	int					m_nVST;
	int					MAX_BUFFER_SIZE;
	int					MIN_BUFFER_SIZE;
	BOOL				m_fVstChanged;
public:
	/** The standard methods.
	 */
	CAudioHostForCA(void);
	~CAudioHostForCA(void);
	int GetListenDeviceCount(void);
	int GetCaptureDeviceCount(void);
	char* GetListenDeviceName(int i);
	char* GetCaptureDeviceName(int i);
	int GetCurrentListenDevice(void);
	BOOL SetCurrentListenDevice(int i);
	BOOL SetCurrentListenDeviceByName(char* szDeviceName);
	int GetCurrentCaptureDevice(void);
	BOOL SetCurrentCaptureDevice(int i);
	BOOL SetCurrentCaptureDeviceByName(char* szDeviceName);
	BOOL Start(void);
	void Stop(void);
	BOOL SetBufferSize(int size);
	int GetBufferSize(void);
	int GetBufferSizeBySampleCount(){return m_nBufferSize/m_wfx.Format.nBlockAlign;};
	BOOL SetBufferSizeBySampleCount(int samples){return SetBufferSize(samples*m_wfx.Format.nBlockAlign);};
	int SetVolume(int Vol);
	int GetVolume(void);
	BOOL SetFormat(WAVEFORMATEX* pwfx);
	void GetFormat(WAVEFORMATEX* pwfx);

	BOOL SetVstEffect(CWavVstHost** pVSTArray,int nCount);

	BOOL UseDirectSoundOutput(BOOL fUseDirectSoundOutput);

public:
	/** The implement of inherited COM methods
	 */
	
	/** For IUnknown
	 */
	STDMETHOD(QueryInterface)(REFIID iid, void **pvObject);
	STDMETHOD_(ULONG, AddRef)();
    STDMETHOD_(ULONG, Release)();
	
	/** For IAudioSessionEvents
	 */
	STDMETHOD(OnDisplayNameChanged) (LPCWSTR /*NewDisplayName*/, LPCGUID /*EventContext*/) { return S_OK; };
    STDMETHOD(OnIconPathChanged) (LPCWSTR /*NewIconPath*/, LPCGUID /*EventContext*/) { return S_OK; };
    STDMETHOD(OnSimpleVolumeChanged) (float NewSimpleVolume, BOOL NewMute, LPCGUID /*EventContext*/);
    STDMETHOD(OnChannelVolumeChanged) (DWORD /*ChannelCount*/, float /*NewChannelVolumes*/[], DWORD /*ChangedChannel*/, LPCGUID /*EventContext*/) { return S_OK; };
    STDMETHOD(OnGroupingParamChanged) (LPCGUID /*NewGroupingParam*/, LPCGUID /*EventContext*/) {return S_OK; };
    STDMETHOD(OnStateChanged) (AudioSessionState /*NewState*/) { return S_OK; };
    STDMETHOD(OnSessionDisconnected) (AudioSessionDisconnectReason DisconnectReason);
    
	/** For IMMNotificationClient
	 */
	STDMETHOD(OnDeviceStateChanged) (LPCWSTR /*DeviceId*/, DWORD /*NewState*/) { return S_OK; }
    STDMETHOD(OnDeviceAdded) (LPCWSTR /*DeviceId*/) { return S_OK; };
    STDMETHOD(OnDeviceRemoved) (LPCWSTR /*DeviceId(*/) { return S_OK; };
	STDMETHOD(OnDefaultDeviceChanged) (EDataFlow Flow, ERole Role, LPCWSTR NewDefaultDeviceId);
    STDMETHOD(OnPropertyValueChanged) (LPCWSTR /*DeviceId*/, const PROPERTYKEY /*Key*/){return S_OK; };
private:
	LONG				_RefCount;
	//Engine can not be used, worst moment!
	BOOL				_fCannotUse;
	//
    //  Core Audio Capture member variables.
    //
    IMMDevice *         _CEndpoint;
	IMMDevice *         _LEndpoint;
    IAudioClient *      _CAudioClient;
	IAudioClient *      _LAudioClient;
    IAudioCaptureClient *_CaptureClient;
	IAudioRenderClient *_RenderClient;
	IAudioEndpointVolume *_Volume;

    HANDLE              _CaptureThread;
    HANDLE              _ShutdownEvent;
    HANDLE              _CAudioSamplesReadyEvent;
	HANDLE              _LAudioSamplesReadyEvent;
	
	WAVEFORMATEX *      _MixFormat;
    size_t              _FrameSize;
    UINT32 _BufferSize;

    //
    //  Capture buffer management.
    //
    BYTE *_CaptureBuffer;
    size_t _CaptureBufferSize;
    size_t _CurrentCaptureIndex;

	//
    //  Stream switch related members and methods.
    //
    bool                    _EnableStreamSwitch;
    ERole                   _CEndpointRole;
	ERole                   _LEndpointRole;
    HANDLE                  _StreamSwitchEvent;          // Set when the current session is disconnected or the default device changes.
    HANDLE                  _StreamSwitchCompleteEvent;  // Set when the default device changed.
    IAudioSessionControl *  _CAudioSessionControl;
	IAudioSessionControl *  _LAudioSessionControl;
    IMMDeviceEnumerator *   _DeviceEnumerator;
    bool                    _InStreamSwitch;

	bool Initialize();
	void Uninitialize();
	static DWORD __stdcall WASAPICaptureThread(LPVOID Context);
    DWORD DoCaptureThread();

	bool SetFmt();
	bool InitializeStreamSwitch();
    void TerminateStreamSwitch();
    bool HandleStreamSwitchEvent();
	void GetDevices();
	bool InitializeAudioEngine();
};

#endif
#endif
